<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h3>使用语义字符串直接构建</h3>
		<ul>
			<li>var obj = {propName:propValue, ......};</li>
			<li>优点：直观，方便，与JSON对象格式相近</li>
			<li>缺点：没有明确的"类型"</li>
			<button onclick="funcTest1()">运行例子</button>
		</ul>
		<script>
			function funcTest1(){
				var employee = {
					id:1001, 
					name:"jerry", 
					gender:"男",
					toString : function(){		
						// 务必要使用this.id，表示是函数的调用者(employee对象)的id属性
						// 否则，将找不到id的定义
						return "id:" + this.id 	
							+ ",name:" + this.name + ",gender:" + this.gender;
					}
				};
				console.log(employee.toString());
			}
		</script>
		
		<h3>使用Object和动态属性构建</h3>
		<ul>
			<li>var obj = new Object; obj.propName = propValue;</li>
			<li>优点：直观，方便</li>
			<li>缺点：没有明确的"类型"</li>
			<button onclick="funcTest2()">运行例子</button>
		</ul>
		<script>
			function funcTest2(){
				var employee = new Object;
				employee.id = 1001;
				employee.name = "jerry"; 
				employee.gender = "男";
				employee.toString = function(){		
					return "id:" + this.id 	
						+ ",name:" + this.name + ",gender:" + this.gender;
				};
				console.log(employee.toString());
			}
		</script>
		
		<h3>使用工厂模式创建对象实例</h3>
		<ul>
			<li>function createObj(...)   var obj = createObj(...)</li>
			<li>优点：能够通过传递不同参数创建不同对象实例；每次创建的实例具有相同的属性名称</li>
			<li>缺点：不像经典面向对象语言中的创建对象的方式</li>
			<button onclick="funcTest3()">运行例子</button>
		</ul>
		<script>
			function createEmployee(id,name,gender){
				var emp = new Object;
				emp.id = id;
				emp.name = name;
				emp.gender = gender;
				emp.toString = function(){		
					return "id:" + this.id 	
						+ ",name:" + this.name + ",gender:" + this.gender;
				};
				return emp;
			}
			function funcTest3(){
				var emp1 = createEmployee(1001,"Jerry","男");
				var emp2 = createEmployee(1002,"Jam","女");
				console.log(emp1.toString());
				console.log(emp2.toString());
			}
		</script>
		
		<h3>构造函数模式</h3>
		<ul>
			<li>function Employee(...)   var obj = Employee(...)</li>
			<li>优点：与经典面向对象语言中的对象创建方式非常贴近</li>
			<li>缺点：每个实例都会创建自己的函数，从内存上来说比较浪费</li>
			<li>定义了构造函数后，instanceof关键字可用来判断某对象是否是该构造函数代表的类型</li>
			<button onclick="funcTest4()">运行例子</button>
		</ul>
		<script>
			function Employee(id,name,gender){
				this.id = id;
				this.name = name;
				this.gender = gender;
				this.toString = function(){		
					return "id:" + this.id 	
						+ ",name:" + this.name + ",gender:" + this.gender;
				};
			}
			function funcTest4(){
				var emp1 = new Employee(1001,"Jerry","男");	
				var emp2 = new Employee(1002,"Jam","女");	
				console.log(emp1.toString());
				console.log(emp2.toString());
				
				// emp1和emp2分别拥有自己的toString拷贝，二者并不指向同一个内存
				console.log(emp1.toString == emp2.toString);
				
				console.log(emp1 instanceof Employee);
				
			}
		</script>
		
		<h3>构造函数与普通函数</h3>
		<ul>
			<li>构造函数就是一个普通函数，只是习惯上使用首字母大写，以表明它代表着某种类型</li>
			<li>在使用new来调用函数时，函数被视为构造函数；如果没有new直接调用，则与普通函数完全相同</li>
			<li>作为构造函数中的this和作为普通函数中的this，指向不同的对象 </li>
			<button onclick="funcTest5()">运行例子</button>
		</ul>
		<script>
			function Employee(id,name,gender){
				this.id = id;
				this.name = name;
				this.gender = gender;
				this.toString = function(){		
					return "id:" + this.id 	
						+ ",name:" + this.name + ",gender:" + this.gender;
				};
			}
			function funcTest5(){
				var emp1 = new Employee(1001,"Jerry","男");	// 当成构造函数调用
				
				// 如果直接调用Employee，则Employee中的this实际上就是window对象
				Employee(1002, "Jam", "女");					// 视为普通函数调用
				console.log(window.toString());
			}
		</script>
		
		<h3>构造函数模式-改进</h3>
		<ul>
			<li>在构造函数外部定义函数，类中定义属性指向外部定义好的函数</li>
			<li>优点：所有对象的成员函数在内存中共享一份，内存形式更优化</li>
			<li>缺点：成员函数必须在外部定义，失去了使用{}进行"封装"的形式意义</li>
			<button onclick="funcTest6()">运行例子</button>
		</ul>
		<script>
			function Employee(id,name,gender){
				this.id = id;
				this.name = name;
				this.gender = gender;
				this.toString = getEmployeeString;
			}
			function getEmployeeString(){
				return "id:" + this.id 	
						+ ",name:" + this.name + ",gender:" + this.gender;
			};	
			function funcTest6(){
				var emp1 = new Employee(1001,"Jerry","男");	
				var emp2 = new Employee(1002,"Jam","女");	
				console.log(emp1.toString());
				console.log(emp2.toString());
				
				// emp1和emp2的toString指向同一个内存
				console.log(emp1.toString == emp2.toString);
			}
		</script>
		
		<h3>原型模式</h3>
		<ul>
			<li>通过prototype来定义函数</li>
			<li>优点：将成员属性和函数封装在{}中，同时内存结构合理</li>
			<button onclick="funcTest7()">运行例子</button>
		</ul>
		<script>
			function Employee(id,name,gender){
				this.id = id;
				this.name = name;
				this.gender = gender;
				Employee.prototype.toString = function(){
					return "id:" + this.id 	
							+ ",name:" + this.name + ",gender:" + this.gender;
				};
			}
			// 也可以在Employee{}外部定义Employee.prototype.toString
			
			function funcTest7(){
				var emp1 = new Employee(1001,"Jerry","男");	
				var emp2 = new Employee(1002,"Jam","女");	
				console.log(emp1.toString());
				console.log(emp2.toString());
				
				// emp1和emp2的toString指向同一个内存
				console.log(emp1.toString == emp2.toString);
			}
		</script>
		
		<h3>Prototype的内存结构</h3>
		<ul>
			<li>通过prototype定义的属性或函数，在内存中只存一份，对于某个类的所有对象实例共享，可视为"静态成员"</li>
			<li>通过this直接定义的属性或函数，在内存中会为每个对象分别存放。可视为"实例成员"</li>
			<li>若某个属性prop仅通过prototype进行了定义，但之后又通过对象实例(而不是prototype)直接修改了改属性值，则会在该对象中自动添加同名的实例属性</li>
			<li>通过"对象名.属性名"(例如emp.name)访问时,优先查找实例属性name，如果没找到，则再查找prototype属性name</li>
			<button onclick="funcTest8()">运行例子</button>
		</ul>
		<script>
			function Employee(id,name,gender){
				this.id = id;
				Employee.prototype.name = name;
				this.gender = gender;
			}
			
			function funcTest8(){
				var emp1 = new Employee(1001,"Jerry","男");	
				var emp2 = new Employee(1002,"Jam","女");	
				emp1.id = 2001;
				console.log(emp2.id);		// id是实例属性，emp1和emp2各自分别拥有一份
				
				Employee.prototype.name = "david";		// 通过prototype修改属性值
				console.log(emp2.name);		// name被定义为prototype属性，emp1和emp2共享
				
				emp1.name = "tom";			// 为emp1对象实例添加一个实例属性name
				console.log(emp2.name);		// emp2对象仍然没有实例属性name，只有prototype属性name
			}
		</script>
	</body>
</html>
